package player_single

import (
	"strings"
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	"gddgame.cc/galaxy/common/dsl/game/user"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/utils/orm"
)

type PlayerSingleData struct {
	env       *game.Environment
	Singles   []PlayerSingle
	SingleMap map[string]*PlayerSingle

	lastSingleUpdateTime *time.Time

	singleModel     *orm.Model
	userSingleModel *orm.Model

	SingleLeaderboard string
}

func GetPlayerSingleData(env *game.Environment) (*PlayerSingleData, error) {
	var err error
	data := &PlayerSingleData{env: env}
	if data.singleModel, data.Singles, data.SingleMap, err = getSingleData(env); err != nil {
		return nil, err
	}
	if data.userSingleModel, err = env.GetModel(PlayerUserSingleName); err != nil {
		return nil, err
	}
	if err := env.Config(SingleLeaderboard, &data.SingleLeaderboard); err != nil {
		return nil, err
	}

	for _, single := range data.Singles {
		if data.lastSingleUpdateTime == nil || single.UpdateTime.After(*data.lastSingleUpdateTime) {
			data.lastSingleUpdateTime = single.UpdateTime
		}
	}
	return data, nil
}

func getSingleData(env *game.Environment) (*orm.Model, []PlayerSingle, map[string]*PlayerSingle, error) {
	var err error
	var singles []PlayerSingle

	model, err := env.GetModel(PlayerSingleName)
	if err != nil {
		return nil, nil, nil, err
	}
	_, err = model.Query().Find(&singles)
	if err != nil {
		return nil, nil, nil, err
	}
	singleMap := make(map[string]*PlayerSingle, len(singles))
	for k := range singles {
		singleMap[singles[k].Key] = &singles[k]
	}
	return model, singles, singleMap, nil
}

func (data *PlayerSingleData) GetAllSingle(userOpenId string, userId int, updateTime *time.Time) ([]*PlayerSingleExtend, error) {
	var err error
	var uSingles []PlayerUserSingle
	query := data.userSingleModel.Query().Where("=", "user_id", userId)
	if updateTime != nil {
		ids := []string{}
		for key := range data.Singles {
			single := &data.Singles[key]
			if updateTime.Before(*single.UpdateTime) {
				ids = append(ids, single.Key)
			}
		}
		if len(ids) > 0 {
			query.WhereSql("(update_time > ? or single_key in ('"+strings.Join(ids, "','")+"'))", updateTime)
		} else {
			query.Where(">", "update_time", updateTime)
		}
	}
	if _, err = query.Find(&uSingles); err != nil {
		return nil, SystemExceptionDetail("查找用户关卡失败", err)
	}
	uSingleMap := make(map[string]*PlayerUserSingle, len(uSingles))
	for k := range uSingles {
		uSingleMap[uSingles[k].SingleKey] = &uSingles[k]
	}
	// 获取道具信息
	singles := make([]*PlayerSingleExtend, 0, 10)
	for key := range data.Singles {
		single := &data.Singles[key]
		if u, ok := uSingleMap[single.Key]; ok {
			u.Rank, _ = data.env.NumberLeaderboard(data.env.ID(), userOpenId, single.LeaderboardKey)
			singles = append(singles, NewPlayerSingleExtend(single, u))
		} else if updateTime == nil || updateTime.Before(*single.UpdateTime) {
			singles = append(singles, NewPlayerSingleExtend(single, nil))
		}
	}
	return singles, nil
}

func (data *PlayerSingleData) InsertSingleResult(user user.UserBase, singleKey string, result *SingleResult) error {
	if _, err := data.userSingleModel.Insert(&PlayerUserSingle{
		UserId:    user.GetId(),
		SingleKey: singleKey,
		Times:     1,
		Star:      result.Star,
		Pass:      result.Pass,
		LastTime:  result.Time,
		BestTime:  result.Time,
		LastScore: result.Score,
		BestScore: result.Score,
	}); err != nil {
		return SystemExceptionDetail("更新关卡失败", err)
	}
	return nil
}

func (data *PlayerSingleData) FinishSingle(user user.UserBase, singleKey string, currentSingleScore int, result *SingleResult, userData map[string]interface{}) (*SingleResult, error) {
	var userSingle PlayerUserSingle
	var single PlayerSingle
	if ok, err := data.singleModel.PK(singleKey).One(&single); !ok {
		return nil, err
	}
	singleData := map[string]interface{}{}
	if single.BestScore < result.Score {
		single.BestScore = result.Score
		singleData["best_score"] = result.Score
	}
	if single.BestTime > result.Time || single.BestTime == 0 {
		single.BestTime = result.Time
		singleData["best_time"] = result.Time
	}
	if len(singleData) > 0 {
		if _, err := data.singleModel.PK(singleKey).Update(singleData); err != nil {
			return nil, err
		}
		data.SingleMap[singleKey] = &single
	}
	if ok, _ := data.userSingleModel.Query().
		Where("=", "user_id", user.GetId()).
		Where("=", "single_key", singleKey).One(&userSingle); ok {
		updateData := make(map[string]interface{})
		updateData["times"] = &orm.UpdateOperate{Operate: "+", Value: 1}

		singleRank, err := data.env.NumberLeaderboard(data.env.ID(), user.GetOpenId(), single.LeaderboardKey)
		if err != nil {
			singleRank = 0
		}
		before := &SingleResult{
			Star:  userSingle.Star,
			Pass:  userSingle.Pass,
			Score: userSingle.BestScore,
			Time:  userSingle.BestTime,
			Rank:  singleRank,
		}
		result.Rank = before.Rank
		if !userSingle.Pass && result.Pass {
			updateData["pass"] = true
		} else if !result.Pass && userSingle.Pass {
			result.Pass = true
		}
		if userSingle.Star < result.Star {
			userData["single_star"] = &orm.UpdateOperate{Operate: "+", Value: result.Star - userSingle.Star}
			updateData["star"] = result.Star
		} else {
			result.Star = userSingle.Star
		}
		if userSingle.BestTime == 0 || userSingle.BestTime > result.Time {
			updateData["best_time"] = result.Time
		} else {
			result.Time = userSingle.BestTime
		}
		updateData["last_time"] = result.Time
		if userSingle.BestScore < result.Score {
			updateData["best_score"] = result.Score

			// 更新排行榜
			if err := data.env.UpdateLeaderboard(data.env.ID(), user.GetOpenId(), single.LeaderboardKey, result.Score); err != nil {
				return result, err
			}
			if err := data.env.UpdateLeaderboard(data.env.ID(), user.GetOpenId(), data.SingleLeaderboard, currentSingleScore+result.Score-userSingle.BestScore); err != nil {
				return result, err
			}
			singleRank, err := data.env.NumberLeaderboard(data.env.ID(), user.GetOpenId(), single.LeaderboardKey)
			if err != nil {
				singleRank = 0
			}
			result.Rank = singleRank
			userData["single_score"] = &orm.UpdateOperate{Operate: "+", Value: result.Score - userSingle.BestScore}
		} else {
			result.Score = userSingle.BestScore
		}
		updateData["last_score"] = result.Score
		if len(updateData) > 0 {
			if _, err := data.userSingleModel.PK(userSingle.Id).Update(updateData); err != nil {
				return nil, SystemExceptionDetail("更新关卡失败", err)
			}
		}

		return before, nil
	}
	if err := data.InsertSingleResult(user, singleKey, result); err != nil {
		return nil, err
	}
	userData["single_score"] = &orm.UpdateOperate{Operate: "+", Value: result.Score}
	userData["single_star"] = &orm.UpdateOperate{Operate: "+", Value: result.Star}

	// 更新排行榜
	_ = data.env.UpdateLeaderboard(data.env.ID(), user.GetOpenId(), single.LeaderboardKey, result.Score)
	_ = data.env.UpdateLeaderboard(data.env.ID(), user.GetOpenId(), data.SingleLeaderboard, currentSingleScore+result.Score)
	singleRank, err := data.env.NumberLeaderboard(data.env.ID(), user.GetOpenId(), single.LeaderboardKey)
	if err != nil {
		singleRank = 0
	}
	result.Rank = singleRank
	return &SingleResult{
		Star:  0,
		Pass:  false,
		Score: 0,
		Time:  0,
		Rank:  0,
	}, nil
}
